#include <STDIO.H>
#include <WINDOWS.H>
#include "Server\prime.h"
#include "directio.h"

/* If we found a prime then return PRIME, otherwise return NOT_PRIME */

#define PRIME           1
#define NOT_PRIME       0

/* Maximum Number of Clients to respond to */

#define MAX_CALLS      15

/* Coordinates for displaying numbers to be tested */

#define TESTING_X1      3
#define TESTING_Y1      3
#define TESTING_X2     23
#define TESTING_Y2     18

/* Coordinates for displaying prime numbers */

#define PRIME_X1       30
#define PRIME_Y1        3
#define PRIME_X2       50
#define PRIME_Y2       18

#define ERROR_EXIT      2
#define SUCCESS_EXIT    0
#define STRING_LENGTH 256

/* Time delay in microseconds */

#define WAIT          350
#define WAIT_DISPLAY 2000

/* Colors used in this application */

#define WHITE_ON_BLUE FOREGROUND_WHITE|FOREGROUND_INTENSITY|BACKGROUND_BLUE
#define WHITE_ON_CYAN FOREGROUND_WHITE|FOREGROUND_INTENSITY|BACKGROUND_CYAN
#define RED_ON_BLUE   FOREGROUND_RED|FOREGROUND_INTENSITY|BACKGROUND_BLUE
#define RED_ON_CYAN   FOREGROUND_RED|FOREGROUND_INTENSITY|BACKGROUND_CYAN
#define CYAN_ON_CYAN  FOREGROUND_CYAN|BACKGROUND_CYAN

/* Number of characters in computer name to display on the screen */

#define COMP_NAME_LENGTH 7

extern CRITICAL_SECTION GlobalCriticalSection;
unsigned long handles = 0, handlesp = 0;

/* Critical Section for choosing next available number for testing */

char GlobalComputerNameBuffer[MAX_CALLS][MAX_COMPUTERNAME_LENGTH];

/* Number of primes for each client */

unsigned long NoPrimes[MAX_CALLS];
unsigned long GlobalComputerHandleBuffer[MAX_CALLS];
unsigned long handle_mod = 1;

/*
   This function is called by the client, we return a unique number,
   so in the future we can keep track who called us
*/

unsigned long InitializePrimeServer(handle_t h1,
   PPCONTEXT_HANDLE_TYPE pphContext,unsigned char *ComputerName)
   {
   char Buffer[STRING_LENGTH];
   unsigned long unique_handle;

   EnterCriticalSection(&GlobalCriticalSection);
   unique_handle = handles + 10 * handle_mod++;

   strcpy(GlobalComputerNameBuffer[handles], ComputerName);

   mxyputc(2, 21, (char)32, 75, CYAN_ON_CYAN);
   sprintf(Buffer, "Computer %s logged in.", ComputerName);
   mxyputs(27, 21, Buffer, RED_ON_CYAN);
   sprintf(Buffer,  "(%ld)",unique_handle);
   mxyputs(54, 21, Buffer, RED_ON_CYAN);
   Sleep(2*WAIT_DISPLAY);
   mxyputc(2, 21, (char)32, 75, CYAN_ON_CYAN);

   GlobalComputerHandleBuffer[handles] = unique_handle;

   *pphContext = (PCONTEXT_HANDLE_TYPE)unique_handle;

   NoPrimes[handles] = 0;

   handles++;
   handlesp = handles;
   LeaveCriticalSection(&GlobalCriticalSection);

   return unique_handle;
   }

/*
   This function was called by the client. The client supplied his identifier
   and the number to test if it is a prime
*/

unsigned char RemoteIsPrime(handle_t h1, unsigned long PrimeServerHandle,
   unsigned long TestNumber)
   {
   unsigned long count;
   unsigned long HalfNumber = TestNumber / 2 + 1;
   char Buffer[STRING_LENGTH];
   char LocalComputerName[MAX_COMPUTERNAME_LENGTH];
   int loop;

   SMALL_RECT psrctScrollRectTesting, psrctScrollRectPrime;
   COORD coordDestOriginTesting, coordDestOriginPrime;
   CHAR_INFO pchiFill;
   WORD Local[COMP_NAME_LENGTH];

   COORD ComputTestCoord;
   COORD ComputPrimeCoord;
   DWORD dummy;

/* Lets find out the name of client computer */

   EnterCriticalSection(&GlobalCriticalSection);
   for(loop = 0; loop < (int)handles; loop++)
      if(GlobalComputerHandleBuffer[loop] == PrimeServerHandle)
         {
         strcpy(LocalComputerName, GlobalComputerNameBuffer[loop]);
         PrimeServerHandle = loop;
         break;
         }

   LeaveCriticalSection(&GlobalCriticalSection);

   psrctScrollRectTesting.Left   = TESTING_X1+1;
   psrctScrollRectTesting.Top    = TESTING_Y1+2;
   psrctScrollRectTesting.Right  = TESTING_X2-1;
   psrctScrollRectTesting.Bottom = TESTING_Y2-1;

   coordDestOriginTesting.X = TESTING_X1+1;
   coordDestOriginTesting.Y = TESTING_Y1+1;

   psrctScrollRectPrime.Left   = PRIME_X1+1;
   psrctScrollRectPrime.Top    = PRIME_Y1+2;
   psrctScrollRectPrime.Right  = PRIME_X2-1;
   psrctScrollRectPrime.Bottom = PRIME_Y2-1;

   coordDestOriginPrime.X = PRIME_X1+1;
   coordDestOriginPrime.Y = PRIME_Y1+1;

   pchiFill.Char.AsciiChar = (char)32;
   pchiFill.Attributes = WHITE_ON_BLUE;

   ComputTestCoord.X  = TESTING_X2-7;
   ComputTestCoord.Y  = TESTING_Y2-1;
   ComputPrimeCoord.X = PRIME_X2-7;
   ComputPrimeCoord.Y = PRIME_Y2-1;

   for(loop = 0; loop < COMP_NAME_LENGTH; loop++)
      Local[loop] = WHITE_ON_BLUE;

   if(VK_ESCAPE == get_character_no_wait())
      {

/* If the user pressed ESC then exit */

      EnterCriticalSection(&GlobalCriticalSection);
      Sleep(WAIT);
      clearscreen(0);
      exit(SUCCESS_EXIT);
      LeaveCriticalSection(&GlobalCriticalSection);
      DeleteCriticalSection(&GlobalCriticalSection);
      }

   sprintf(Buffer, "%d", TestNumber);

   EnterCriticalSection(&GlobalCriticalSection);
   ScrollConsoleScreenBuffer(hStdOut, &psrctScrollRectTesting, NULL,
      coordDestOriginTesting, &pchiFill);
   sprintf(Buffer, "%d", TestNumber);
   mxyputs((unsigned char)TESTING_X1+2, (unsigned char)(TESTING_Y2-1),
      Buffer, WHITE_ON_BLUE);
   WriteConsoleOutputAttribute(hStdOut, Local, COMP_NAME_LENGTH,
      ComputTestCoord, &dummy);
   LocalComputerName[COMP_NAME_LENGTH] = 0;
   mxyputs((unsigned char)(TESTING_X2-7), (unsigned char)TESTING_Y2-1,
      LocalComputerName, WHITE_ON_BLUE);
   LeaveCriticalSection(&GlobalCriticalSection);

   for(count = 2; count < HalfNumber; count++)
      if(TestNumber % count == 0)
         return NOT_PRIME;

   sprintf(Buffer, "%d", TestNumber);

   EnterCriticalSection(&GlobalCriticalSection);
   ScrollConsoleScreenBuffer(hStdOut, &psrctScrollRectPrime, NULL,
      coordDestOriginPrime, &pchiFill);
   sprintf(Buffer, "%d", TestNumber);
   mxyputs((unsigned char)(PRIME_X1+2), (unsigned char)PRIME_Y2-1, Buffer,
      WHITE_ON_BLUE);
   WriteConsoleOutputAttribute(hStdOut, Local, COMP_NAME_LENGTH,
      ComputPrimeCoord, &dummy);
   mxyputs((unsigned char)(PRIME_X2-7), (unsigned char)PRIME_Y2-1,
      LocalComputerName, WHITE_ON_BLUE);
   NoPrimes[PrimeServerHandle]++;
   LeaveCriticalSection(&GlobalCriticalSection);

   return PRIME;
   }

/* Notification from a client that he is done */

void TerminatePrimeServer(handle_t h1, unsigned long PrimeServerHandle)
   {
   char Buffer[256+MAX_COMPUTERNAME_LENGTH];
   unsigned char loop, loopshift;

   EnterCriticalSection(&GlobalCriticalSection);

   for(loop = 0; loop < (unsigned char)handles; loop++)
      {
      if(GlobalComputerHandleBuffer[loop] == PrimeServerHandle)
         {
         mxyputc(2, 21, (char)32, 75, CYAN_ON_CYAN);

/* Let's put terminating message on the screen */

         sprintf(Buffer, "Computer %s Exited!",
            GlobalComputerNameBuffer[loop]);
         mxyputs(27, 21, Buffer, RED_ON_CYAN);
         mxyputc(59, (unsigned char)(4+loop), (char)32, 15, CYAN_ON_CYAN);
         Sleep(WAIT_DISPLAY);
         mxyputc(2, 21, (char)32, 75, CYAN_ON_CYAN);
         break;
         }
      }

   if(loop < handles-1)
      {
      loopshift = loop;
      for(loop = loopshift; loop < (unsigned char)(handles - 1); loop++)
         {
         strcpy(GlobalComputerNameBuffer[loop],
            GlobalComputerNameBuffer[loop+1]);
         GlobalComputerHandleBuffer[loop] =
            GlobalComputerHandleBuffer[loop+1];
         NoPrimes[loop] = NoPrimes[loop+1];
         }
      }

/* Let's remove his name from our table */

   handles--;
   LeaveCriticalSection(&GlobalCriticalSection);
   }

void __RPC_USER PCONTEXT_HANDLE_TYPE_rundown(PCONTEXT_HANDLE_TYPE phContext)
   {
   handle_t dummmy = 0;

   if(handlesp == handles)
      {
      mxyputs(27, 2, "rundown Executed", RED_ON_CYAN);
      TerminatePrimeServer(dummmy, (unsigned long)phContext);
      Sleep(2000);
      mxyputs(27, 2, "                ", CYAN_ON_CYAN);
      }

   handlesp = handles;
   }
